<?php

namespace app\api\service;

use app\api\model\Oauth;
use app\api\service\wx\WXBizDataCrypt;
use app\api\validate\Phone;
use app\common\controller\Send;
use app\api\model\Phone as PhoneModel;
use think\facade\Log;
class UserToken extends Token
{
    use Send;

    protected $appId;
    protected $appSecret;
    protected $apiUrl = 'https://api.weixin.qq.com';
    protected $refreshPrefix = 'token_refresh';
    protected $expireIn;
    protected $refreshExpireIn;


    public function __construct()
    {
        $wx = config('app.wx_mini');
        $setting = config('app.setting');
        $this->appId = $wx['app_id'];
        $this->appSecret = $wx['app_secret'];
        $this->expireIn = $setting['token_expire_in'];
        $this->refreshExpireIn = $setting['refresh_expire_in'];
    }

    /**
     * 小程序登录
     * @param $code
     * @return array
     */
    public function login($code)
    {
        $param = [
            'appid' => $this->appId,
            'secret' => $this->appSecret,
            'js_code' => $code,
            'grant_type' => 'authorization_code',
        ];

        $query = http_build_query($param);
        $url = $this->apiUrl . '/sns/jscode2session?' . $query;

        $result = curl_get($url);
        
        $result = json_decode($result, true);
        
        if (empty($result)) {
            // 经验之谈：返回结果为空[没返回错误信息和错误代码]，是微信服务器接口的问题
            return self::sendResult(101, [], '小程序登录失败，微信内部错误！');
        } else {
            // 微信返回错误码
            if (isset($result['errcode'])) {
                return self::sendResult(101, [], $result['errmsg']);
            } else {
                return self::sendResult(self::$okCode, $result);
            }
        }
    }

    /**
     * 缓存微信用户数据，并返回token令牌
     * @param $wxResult
     * @return array
     * @throws \think\Exception\DbException
     */
    public function grantToken($wxResult)
    {
        // 1.拿到微信返回的openid(用户唯一标识)
        $openid = $wxResult['openid'];
        $unionid = array_key_exists("unionid",$wxResult) ? $wxResult['unionid'] : null;

        $oauthUnionid = $unionid == null ? null : Oauth::getByUnionid($unionid);
        // 2.查找数据库中该openid的记录是否存在
        $oauth = Oauth::getByOpenId($openid);
        
        // 3.如果存在，则不处理； 如果不存在，则新增一条用户记录
        if ($oauth) {
            $uid = $oauth->id;
        } else {
            if($oauthUnionid){
                Log::write($wxResult, 'reqDataxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
                Oauth::updateOpenid($unionid,$openid);
                $uid = $oauthUnionid['id'];
            }else{
                $uid = Oauth::addOne2($openid,$unionid);
            }
            //$uid = Oauth::addOne($openid);
        }
        
        // 4.生成令牌，准备缓存数据，写入缓存，并返回token [获取用户的相关信息]
        $tokenValue = $this->tokenValue($wxResult, $uid);
        return $this->saveToken($tokenValue);
    }

    /**
     * 需要缓存的数据
     * @param array $wxResult 微信返回的数组 openid 和 session_key(会话密钥),unionid(在开放平台的唯一标识符)在满足 UnionID 下发条件的情况下会返回
     * @param int $uid 授权的用户id
     * @return mixed
     */
    private function tokenValue($wxResult, $uid)
    {
        // openid + session_key + unionid(有的话) + uid
        $cachedValue = $wxResult;
        $cachedValue['uid'] = $uid;
        return $cachedValue;
    }

    /**
     * 微信用户数据写入缓存
     * @param array $cachedValue 需要缓存的微信用户数据
     * @return array
     */
    private function saveToken($cachedValue)
    {
        // 生成token
        $key = self::generateToken();
        $refreshKey = self::generateToken();
        // 缓存的微信用户数据
        $value = json_encode($cachedValue);
        // 设置缓存失效时间，比提供的过期时间晚10分钟
        $expireIn = $this->expireIn;
        $refreshExpireIn = $this->refreshExpireIn;
        // 缓存数据
        $request = cache($key, $value, $expireIn);
        $refreshRequest = cache($refreshKey, $key, $refreshExpireIn);

        

	PhoneModel::login(true,$cachedValue['uid'],$key);
        if (!$request || !$refreshRequest) {
            // 令牌缓存出错
            return self::sendResult(103);
        }

        return self::sendResult(self::$okCode, [
            'openid' => $cachedValue['openid'],
            'token' => $key,
            'expire' => time() + $expireIn,
            'refreshToken' => $refreshKey,
        ]);
    }

    /**
     * Token以旧换新
     * @param $refreshToken
     * @return array
     */
    public function refresh($refreshToken)
    {
        // 缓存不存在刷新令牌
        $token = cache($refreshToken);
        if (!$token) {
            return self::sendResult(138);
        }

        // 获取token存的数据
        $value = cache($token);
        if (empty($value)) {
            return self::sendResult(104);
        }
        return $this->saveToken(json_decode($value, true));
    }

    /**
     * 解密数据
     * @param $sessionKey
     * @param $encryptedData
     * @param $iv
     * @return array
     */
    public function decryptData($sessionKey, $encryptedData, $iv)
    {
        $pc = new WXBizDataCrypt($this->appId, $sessionKey);
        $errCode = $pc->decryptData($encryptedData, $iv, $data);
        if ($errCode == 0) {
            $code = self::$okCode;
            
            $data = json_decode($data, true);
            $msg = '';
        } else {
            $code = 105;
            $data = [];
            $msg = '解密数据错误' . $errCode;
        }
        return self::sendResult($code, $data, $msg);
    }

    /**
     * 获取用户信息
     * @param $sessionKey
     * @param $encryptedData
     * @param $iv
     * @return array
     * @throws \think\Exception\DbException
     */
    public function getUserInfo($sessionKey, $encryptedData, $iv)
    {
        $res = $this->decryptData($sessionKey, $encryptedData, $iv);
        if ($res['code'] != self::$okCode) {
            return self::sendResult($res['code'], $res['data'], $res['msg']);
        }

        $data = $res['data'];
        //Log::write($data, 'reqData22222xxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
        $updateData = [
            'openid' => $data['openId'],
            'nickname' => $data['nickName'],
            'sex' => $data['gender'],
            'province' => $data['province'],
            'city' => $data['city'],
            'country' => $data['country'],
            'head_img' => $data['avatarUrl'],
            'unionid'=>$data['unionId']
        ];

        // 验证数据
        $validate = new \app\common\validate\Oauth();
        if (!$validate->check($updateData)) {
            return self::sendResult(102, [], $validate->getError());
        }

        // 更新或者新增用户数据
        $oauth = Oauth::updateOne($updateData['openid'], $updateData);

        return self::sendResult(self::$okCode, [
            'uid' => $oauth['id'],
            'nickname' => $oauth['nickname'],
            'sex' => $oauth['sex'],
            'province' => $oauth['province'],
            'city' => $oauth['city'],
            'country' => $oauth['country'],
            'headImg' => $oauth['head_img'],
            'shareKey' => $oauth['share_key'],
            'autoplay' => $oauth['autoplay'],
            'hasMobile' => $oauth['phone'] ? 1 : 0,
        ]);
    }

    /**
     * 绑定手机号码
     * @param $openid
     * @param $sessionKey
     * @param $encryptedData
     * @param $iv
     * @return array
     * @throws \think\Exception\DbException
     */
    public function getPhone($openid, $sessionKey, $encryptedData, $iv)
    {
        $res = $this->decryptData($sessionKey, $encryptedData, $iv);
        if ($res['code'] != self::$okCode) {
            return self::sendResult($res['code'], $res['data'], $res['msg']);
        }

        $data = $res['data'];

        $updateData = [
            'phone' => $data['phoneNumber'],
            'pure_phone' => $data['purePhoneNumber'],
        ];

        // 绑定手机号码
        $res = Oauth::updatePhone($openid, $updateData);
        return self::sendResult($res ? self::$okCode : 106);
    }
}
